/*
 * @copyright: Huang Ding
 * @Author: ding-cx
 * @Date: 2021-12-12 17:35:55
 * @LastEditors: ding-cx
 * @LastEditTime: 2021-12-13 11:14:42
 * @Description: file content
 */
import { Application, Router } from "egg";

import "reflect-metadata";
import { getRouteMetaData, MiddlewaresType, routeSymbolKey } from "./routeMetadata";

const controllerMappingKey = Symbol.for("dinegg#controllerMapping");

type routeMethod = "get" | "post" | "put" | "del" | "patch";

/**
 * factory create a route bind . decorator for class method.
 */
function createRoute(
	method: routeMethod = "get",
	path: string | string[],
	middlewares?: MiddlewaresType
) {
	// 24.10.26 use Reflect Metadata
	return function (target: any, name: string, descriptor: PropertyDescriptor) {
		if (typeof descriptor.value !== "function") return;

		let routeMetadata = getRouteMetaData(target.constructor);

		if (!routeMetadata[name]) {
			routeMetadata[name] = [];
		}

		if (Array.isArray(path)) {
			for (const pathItem of path) {
				routeMetadata[name].push({
					method: method,
					path: pathItem,
					middlewares,
				});
			}
		} else {
			routeMetadata[name].push({ method: method, path, middlewares });
		}
	};
	// return function (target: any, name: string, descriptor: PropertyDescriptor) {
	// 	if (typeof descriptor.value !== "function") return;

	// 	if (!descriptor.value[routeSymbolKey]) {
	// 		descriptor.value[routeSymbolKey] = [];
	// 	}
	// 	if (Array.isArray(path)) {
	// 		for (const pathItem of path) {
	// 			descriptor.value[routeSymbolKey].push({
	// 				method: method,
	// 				path: pathItem,
	// 				middlewares,
	// 			});
	// 		}
	// 	} else {
	// 		descriptor.value[routeSymbolKey].push({ method: method, path, middlewares });
	// 	}
	// };
}

/** 装饰器，get请求 
 */
export function get(path: string | string[], middlewares?: MiddlewaresType) {
	return createRoute("get", path, middlewares);
}
export const Get = get;

/** 装饰器,post请求
 */
export function post(path: string | string[], middlewares?: MiddlewaresType) {
	return createRoute("post", path, middlewares);
}
export const Post = post;

/** 装饰器,put请求
 */
export function put(path: string | string[], middlewares?: MiddlewaresType) {
	return createRoute("put", path, middlewares);
}
export const Put = put;

/** 装饰器,del请求
 */
export function del(path: string | string[], middlewares?: MiddlewaresType) {
	return createRoute("del", path, middlewares);
}
export const Delete = del;

/** 装饰器,patch请求 */
export function patch(path: string | string[], middlewares?: MiddlewaresType) {
	return createRoute("patch", path, middlewares);
}

export const Patch = patch;

/**
 * 装饰在控制器类上
 * - 实现控制器提供路由前缀能力
 * - 方法装饰器上的路由必须不带/开头，才会拼接，否则该路由是直接注册
 */
export function Controller(opt: { mapping?: string[];[key: string]: any }) {
	return function (constructor: Function) {
		constructor[controllerMappingKey] = opt.mapping as string[];
	};
}

/** 
 * @deprecated 请使用Controller
 */
export const controller = Controller;

// ------------------------------------------------------

/** 递归将绑定在controller method方法上的路由绑定在router上，此函数只能在router.ts中调用。 */
function reduceControllerMethod(router: Router, controller: any) {
	if (Object.prototype.toString.call(controller) !== "[object Object]") {
		//若非对象，结束
		return;
	}
	const property = Object.keys(controller);

	// 遍历控制器对象的属性
	property.forEach((item) => {
		if (!(controller[item] instanceof Function)) {
			return reduceControllerMethod(router, controller[item]);
		}
		//若函数，注册路由 note : 此处之所以能拿到，是因为dinegg重写了控制器的loader
		const routeObjArr: { method: string; path: string; middlewares?: any[] }[] =
			controller[item][routeSymbolKey];

		if (!routeObjArr || !Array.isArray(routeObjArr)) {
			return;
		}
		const mappingValue: string[] = controller[controllerMappingKey];
		const createRouterPathList = () => {
			const paths: any[] = [];
			routeObjArr.forEach((route) => {
				// 如果路由前面以/开头，则不拼接控制器上的前缀
				if (route.path.charAt(0) === "/") {
					paths.push(route);
					return;
				}
				if (mappingValue && Array.isArray(mappingValue)) {
					mappingValue.forEach((prefix) => {
						const newRoute = { ...route };
						if (prefix.endsWith("/")) {
							newRoute.path = `${prefix}${newRoute.path}`;
						} else {
							newRoute.path = `${prefix}/${newRoute.path}`;
						}
						paths.push(newRoute);
					});
					return;
				}
				// 如果path前面没有/开头，而且没有控制器前缀，则自动加上
				route.path = `/` + route.path;
				paths.push(route);
			});
			return paths;
		};

		for (const routeObj of createRouterPathList()) {
			let routePath = routeObj.path;

			if (routeObj.middlewares) {
				router[routeObj.method](routePath, ...routeObj.middlewares, controller[item]);
			} else {
				router[routeObj.method](routePath, controller[item]);
			}
		}
	});
}

/** 在router.ts中调用，绑定装饰器提供的路由
 * 遍历所有的控制器方法然后绑定
 */
export function bindRouter(app: Application) {
	reduceControllerMethod(app.router, app.controller);
}
